import 'package:flame/components.dart';
import 'package:flutter/material.dart';

enum DrawDirection {
  left,
  right,
}

// mm/s
enum WaveSpeed {
  waveSpeed_5,
  waveSpeed_10,
  waveSpeed_12P5,
  waveSpeed_25,
  waveSpeed_50;
}

const waveSpeedNames = {
  WaveSpeed.waveSpeed_5: "5mm/s",
  WaveSpeed.waveSpeed_10: "10mm/s",
  WaveSpeed.waveSpeed_12P5: "12.5mm/s",
  WaveSpeed.waveSpeed_25: "25mm/s",
  WaveSpeed.waveSpeed_50: "50mm/s",
};

double waveSpeedValue(WaveSpeed waveSpeed) {
  double speed = 1.0;
  switch (waveSpeed) {
    case WaveSpeed.waveSpeed_5:
      speed = 5.0;
      break;
    case WaveSpeed.waveSpeed_10:
      speed = 10.0;
    case WaveSpeed.waveSpeed_12P5:
      speed = 12.5;
    case WaveSpeed.waveSpeed_25:
      speed = 25.0;
    case WaveSpeed.waveSpeed_50:
      speed = 50.0;
  }
  return speed;
}

WaveSpeed waveSpeedNext(WaveSpeed waveSpeed) {
  WaveSpeed speed = WaveSpeed.waveSpeed_10;
  switch (waveSpeed) {
    case WaveSpeed.waveSpeed_5:
      speed = WaveSpeed.waveSpeed_10;
      break;
    case WaveSpeed.waveSpeed_10:
      speed = WaveSpeed.waveSpeed_12P5;
      break;
    case WaveSpeed.waveSpeed_12P5:
      speed = WaveSpeed.waveSpeed_25;
      break;
    case WaveSpeed.waveSpeed_25:
      speed = WaveSpeed.waveSpeed_50;
      break;
    case WaveSpeed.waveSpeed_50:
      speed = WaveSpeed.waveSpeed_5;
      break;
  }
  return speed;
}

// mm/mv
enum WaveAmp {
  waveAmp_2,
  waveAmp_2P5,
  waveAmp_5,
  waveAmp_10,
  waveAmp_20,
}

const waveAmpNames = {
  WaveAmp.waveAmp_2: "2mm/mv",
  WaveAmp.waveAmp_2P5: "2.5mm/mv",
  WaveAmp.waveAmp_5: "5mm/mv",
  WaveAmp.waveAmp_10: "10mm/mv",
  WaveAmp.waveAmp_20: "20mm/mv",
};

double waveAmpValue(WaveAmp wavAmp) {
  double amp = 1.0;
  switch (wavAmp) {
    case WaveAmp.waveAmp_2:
      amp = 2.0;
      break;
    case WaveAmp.waveAmp_2P5:
      amp = 2.5;
      break;
    case WaveAmp.waveAmp_5:
      amp = 5.0;
      break;
    case WaveAmp.waveAmp_10:
      amp = 10.0;
      break;
    case WaveAmp.waveAmp_20:
      amp = 20.0;
      break;
  }
  return amp;
}

enum WaveMode {
  waveModeReal,
  waveModelRecord,
}

WaveAmp waveAmpNext(WaveAmp wavAmp) {
  WaveAmp amp = WaveAmp.waveAmp_10;
  switch (wavAmp) {
    case WaveAmp.waveAmp_2:
      amp = WaveAmp.waveAmp_2P5;
      break;
    case WaveAmp.waveAmp_2P5:
      amp = WaveAmp.waveAmp_5;
      break;
    case WaveAmp.waveAmp_5:
      amp = WaveAmp.waveAmp_10;
      break;
    case WaveAmp.waveAmp_10:
      amp = WaveAmp.waveAmp_20;
      break;
    case WaveAmp.waveAmp_20:
      amp = WaveAmp.waveAmp_2P5;
      break;
  }
  return amp;
}

class WaveContext {
  // 屏幕像素大小, 每英寸点数
  late double dpiXY;
  // 采样率, 计算波形分段绘制大小，来平滑波形显示
  late int sampleRate;
  // 每毫伏采样值
  late int valPermv;
  // 每个数据所占物理像素大小
  late Vector2 _pixelSize;
  // 绘制区域物理尺寸和当前绘制位置
  late Rect pysicalArea;
  // 数据0值对应的垂直方向绘制位置，数据要求去直流偏移；
  late double _phsicalCenterY;
  // 绘制区域像素宽度
  late int _winWidth;
  // 绘制位置，对应绘制终点；对应最旧的采样点，从此处开始，把drawPoints数据向右绘制，
  // 到达屏幕最右边后折回屏幕最左边重新绘制。具体做法：
  // 根据当前位置到窗口左侧距离和到右侧距离，把一屏数据分成左右两部分，然后
  // 首先从当前位置向左绘制，按照数据从新到旧（从最后到开始）顺序，直到窗口左边，
  // 再从当前后一个位置向右绘制，按照数据从旧到新（从开始到最后）输血，直到窗口右边；
  int drawCursor = 0;
  // 绘制数据，一屏;
  List<int> drawPoints = [];
  // 擦除光标物理宽度
  double eraseWidth = 20.0;
  // 缓存时间
  final double cachedSecs = 2.0;
  // 缓存数据
  List<int> cachedPoints = [];
  // 历史数据
  final double historySecs = 20;
  List<int> historyPoints = [];
  // 是否绘制标尺
  bool drawRulerFlag = true;
  // 波形颜色
  Color waveColor = Colors.green;
  // 波形名称
  late String waveName;
  // 波形速度
  WaveSpeed waveSpeed = WaveSpeed.waveSpeed_10;
  String speedName = '10mm/s';
  // 波形幅度
  WaveAmp waveAmp = WaveAmp.waveAmp_10;
  String ampName = '10mm/mv';
  // 绘制模式
  WaveMode waveMode = WaveMode.waveModeReal;
  // 标记位
  bool cached = false;

  // 波形区域高度
  double waveHeight() {
    return 2 * valPermv * _pixelSize.y + 40;
  }

  // 波形区域宽度
  double waveWidth() {
    if (waveMode == WaveMode.waveModeReal) {
      return double.infinity;
    } else {
      return historySecs * sampleRate * _pixelSize.x + 10;
    }
  }

  // 画笔
  final painter = Paint()
    ..color = Colors.white
    ..style = PaintingStyle.stroke
    ..strokeWidth = 1
    ..isAntiAlias = true;

  // 擦除画笔
  final earsePainter = Paint()
    ..color = Colors.black
    ..style = PaintingStyle.fill;

  // 构造函数
  WaveContext({
    this.waveMode = WaveMode.waveModeReal,
    required this.dpiXY,
    required this.sampleRate,
    required this.valPermv,
    required this.waveName,
  }) {
    setSpeed(WaveSpeed.waveSpeed_12P5);
    setAmp(WaveAmp.waveAmp_10);
    setArea(const Rect.fromLTWH(0, 0, 100, 50));
  }

  // 根据dt时间间隔和采样率，计算应该移动多少采样数据
  int _updateDataSize(double dt) {
    // dt时间采样点数量
    int dlen = (sampleRate * dt).round();
    double m = cachedPoints.length / sampleRate;
    m -= cachedSecs;
    if (m > 0.5) {
      dlen++;
    } else if (m < 0.5) {
      dlen--;
    }
    if (dlen < 0) dlen = 0;
    return cachedPoints.length < dlen ? cachedPoints.length : dlen;
  }

  // 增加数据到绘制数据，同时移动绘制光标
  void _addDrawPoint(List<int> newPoints) {
    if (newPoints.isEmpty) return;
    drawPoints.addAll(newPoints);
    drawCursor += newPoints.length;
    while (drawPoints.length > _winWidth) {
      drawPoints = drawPoints.sublist(drawPoints.length - _winWidth);
    }
    if (drawCursor > _winWidth) {
      drawCursor %= _winWidth;
    }
  }

  // 屏幕每一帧刷新回调，从缓冲区移动部分数据到绘制数据
  void UpdateData(double dt) {
    if (!cached) {
      if (cachedPoints.length < cachedSecs * sampleRate / 2) {
        return;
      } else {
        cached = true;
      }
    }
    var len = _updateDataSize(dt);
    // print('$dt, $len, ${cachedPoints.length}');
    if (len == 0) {
      return;
    }
    var point = cachedPoints.sublist(0, len);
    _addDrawPoint(point);
    cachedPoints = cachedPoints.sublist(len);
  }

  void setDpi(double dpi) {
    dpiXY = dpi;
    _calPixelSize();
  }

  void setSpeed(WaveSpeed speed) {
    waveSpeed = speed;
    speedName = waveSpeedNames[speed]!;
    _calPixelSize();
    reset();
  }

  void setAmp(WaveAmp amp) {
    waveAmp = amp;
    ampName = waveAmpNames[amp]!;
    _calPixelSize();
  }

  void setDevAttr(int rate, int volpermv) {
    sampleRate = sampleRate;
    valPermv = volpermv;
    _calPixelSize();
  }

  // 根据波形速度/幅度和屏幕DPI，计算采样点在x/y周上像素大小
  void _calPixelSize() {
    _pixelSize = Vector2(1, 1);

    // 每个采样点像素大小
    // speed: 每秒速度，单位mm
    // speed*dpi/25.4: 每秒像素数量
    _pixelSize.x = waveSpeedValue(waveSpeed) * dpiXY / 25.4 / sampleRate;

    // 每个采样值对应的像素大小
    // amp: 每mv数值实际高度, 单位mm
    // amp*dpi/25.4: 每mv像素数量;
    _pixelSize.y = waveAmpValue(waveAmp) * dpiXY / 25.4 / valPermv;
  }

  // 设置绘制区域，重新计算参数
  void setArea(Rect rect) {
    pysicalArea = rect;
    // 距离底部1/3
    _phsicalCenterY = rect.top + rect.height * 1 / 2;
    _winWidth = pysicalArea.width ~/ _pixelSize.x;
    reset();
  }

  // 缓冲数据
  void CacheData(List<int> points) {
    if (waveMode == WaveMode.waveModeReal) {
      cachedPoints.addAll(points);
      // print('cached:${cachedPoints.length}');
      if (cachedPoints.length > 2 * cachedSecs * sampleRate) {
        int dropLen =
            cachedPoints.length - 2 * (cachedSecs * sampleRate).toInt();
        print('drop data:$dropLen');
        cachedPoints = cachedPoints.sublist(dropLen);
      }
    } else {
      historyPoints.addAll(points);
      if (historyPoints.length > historySecs * sampleRate) {
        int dropLen = historyPoints.length - (historySecs * sampleRate).toInt();
        historyPoints = historyPoints.sublist(dropLen);
      }
    }
  }

  // 复位绘制数据
  void reset() {
    drawPoints.clear();
    cachedPoints.clear();
    drawCursor = 0;
    cached = false;
  }

  // 绘制外框
  void drawOutLine(Canvas canvas) {
    canvas.drawRect(pysicalArea, painter);
  }

  void drawWave(Canvas canvas) {
    if (waveMode == WaveMode.waveModeReal) {
      drawRealWave(canvas);
    } else {
      drawRecordWave(canvas);
    }
  }

  // 绘制实时波形
  void drawRealWave(Canvas canvas) {
    // 从当前光标位置分段绘制波形
    List<int> leftPoints =
        drawPoints.sublist(drawPoints.length - drawCursor, drawPoints.length);
    List<int> rightPoints =
        drawPoints.sublist(0, drawPoints.length - drawCursor);
    _drawPoint(
      canvas,
      drawCursor,
      leftPoints,
      DrawDirection.left,
    );
    _drawPoint(
      canvas,
      drawCursor + 1,
      rightPoints,
      DrawDirection.right,
    );
    // 绘制擦除光标
    _drawCursorBar(canvas, pysicalArea, drawCursor * _pixelSize.x, eraseWidth);
  }

  // 绘制实时波形
  void drawRecordWave(Canvas canvas) {
    _drawPoint(
      canvas,
      0,
      historyPoints,
      DrawDirection.right,
    );
  }

  // 绘制标尺
  void drawRuler(Canvas canvas) {
    if (!drawRulerFlag) return;
    // double h = dpiXY / 25.4 * 10; // mm
    double h = _pixelSize.y * valPermv;
    double w = 5; // pixels
    // double yc = phsicalCenterY;
    double x = 35;

    final path3 = Path()
      ..moveTo(x, _phsicalCenterY + h / 2)
      ..lineTo(x - w, _phsicalCenterY + h / 2)
      ..lineTo(x - w, _phsicalCenterY - h / 2)
      ..lineTo(x, _phsicalCenterY - h / 2);

    canvas.drawPath(path3, painter);

    const span = TextSpan(
      style: TextStyle(
        color: Colors.white,
        fontFamily: 'MyriadPro',
      ),
      text: '1mv',
    );
    final tp = TextPainter(
        text: span,
        textAlign: TextAlign.left,
        textDirection: TextDirection.ltr);
    tp.layout();
    tp.paint(canvas, Offset(x, _phsicalCenterY + h / 2));
  }

  void drawName(Canvas canvas) {
    final span = TextSpan(
      style: TextStyle(
        color: waveColor,
        fontFamily: 'MyriadPro',
      ),
      text: waveName,
    );
    final tp = TextPainter(
        text: span,
        textAlign: TextAlign.left,
        textDirection: TextDirection.ltr);
    tp.layout();
    tp.paint(canvas, const Offset(10, 10));
  }

  void drawSpeed(Canvas canvas) {
    final span = TextSpan(
      style: TextStyle(
        color: waveColor,
        fontFamily: 'MyriadPro',
      ),
      text: "$speedName $ampName",
    );
    final tp = TextPainter(
        text: span,
        textAlign: TextAlign.left,
        textDirection: TextDirection.ltr);
    tp.layout();
    tp.paint(canvas, const Offset(50, 10));
  }

  void setDPI(double dpix, double dpiy) {}

  // 擦除区域，相当于光标
  void _drawCursorBar(
    Canvas canvas,
    Rect area,
    double cursorx,
    double width,
  ) {
    if (cursorx + width > area.right) {
      double r = area.right - cursorx;
      double l = width - r;
      var rect1 = Rect.fromLTWH(cursorx, area.top + 1, r, area.height - 2);
      var rect2 = Rect.fromLTWH(area.left, area.top + 1, l, area.height - 2);
      canvas.drawRect(rect1, earsePainter);
      canvas.drawRect(rect2, earsePainter);
    } else {
      var rect = Rect.fromLTWH(cursorx, area.top + 1, width, area.height - 2);
      canvas.drawRect(rect, earsePainter);
    }
  }

  // 绘制一段波形
  void _drawPoint(
    Canvas canvas, // 画布
    int posX,
    List<int> points, // 数据
    DrawDirection dir, // 绘制方向
  ) {
    if (points.isEmpty) return;

    var path = Path();
    if (dir == DrawDirection.left) {
      int i = points.length - 1;
      double fromx = posX * _pixelSize.x + pysicalArea.centerLeft.dx;
      path.moveTo(fromx, _phsicalCenterY - points[i] * _pixelSize.y);
      for (; i >= 0; i--) {
        path.lineTo(fromx, _phsicalCenterY - points[i] * _pixelSize.y);
        fromx = fromx - _pixelSize.x;
        if (fromx <= pysicalArea.left) break;
      }
    } else {
      int i = 0;
      double fromx = posX * _pixelSize.x + pysicalArea.centerLeft.dx;
      path.moveTo(fromx, _phsicalCenterY - points[i] * _pixelSize.y);
      for (; i < points.length; i++) {
        path.lineTo(fromx, _phsicalCenterY - points[i] * _pixelSize.y);
        fromx = fromx + _pixelSize.x;
        if (fromx >= pysicalArea.right) break;
      }
    }

    final painter = Paint()
      ..color = waveColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1
      ..isAntiAlias = true;
    canvas.drawPath(path, painter);
  }
}
